﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Configurator
{
    /// <summary>
    /// Основная форма.
    /// </summary>
    public partial class FormConfigurator : Form
    {
        /// <summary>
        /// Конфигуратор элементов.
        /// </summary>
        private Configurator conf;

        /// <summary>
        /// Инициализирует форму.
        /// </summary>
        /// <param name="listConfiguration">Список элементов конфигурации.</param>
        /// <param name="selectedItem">Выбранный элемент в списке.</param>
        public FormConfigurator(List<Item> listConfiguration, Item selectedItem = null)
        {
            this.InitializeComponent();

            if (null == listConfiguration)
            {
                throw new ArgumentNullException("Начальная конфигурация = null.");
            }

            if (listConfiguration.Count < 1)
            {
                throw new ArgumentException("В начальной конфигурации нет элементов.");
            }

            this.conf = new Configurator();

            this.InitLists(listConfiguration, selectedItem);
        }

        /// <summary>
        /// Возвращает результирующую конфигурацию.
        /// </summary>
        public List<Item> ResultConfiguration
        {
            get
            {
                return this.conf.Items;
            }
        }

        /// <summary>
        /// Инициализирует списки формы.
        /// </summary>
        /// <param name="listConfiguration">Список элементов конфигурации.</param>
        /// <param name="selectedItem">Выбранный элемент в списке.</param>
        private void InitLists(List<Item> listConfiguration, Item selectedItem)
        {
            this.conf.Items = listConfiguration;

            this.conf.SortItem();
            this.conf.SortAllSubItem();

            listItems.DataSource = this.conf.Items;

            listItems.SelectedItem = selectedItem;

            this.UpdateListBox(this.listItems);
            this.UpdateListViewSubItems(false);
        }

        /// <summary>
        /// Обновляет данные в ListBox'е в зависимости от привязки.
        /// </summary>
        /// <param name="listBox">ListBox подлежащий обновлению.</param>
        private void UpdateListBox(ListBox listBox)
        {
            ((CurrencyManager)listBox.BindingContext[listBox.DataSource]).Refresh();
        }

        /// <summary>
        /// Закрывает форму.
        /// </summary>
        /// <param name="cancel">Была ли нажата кнопка "Отмена".</param>
        private void CloseForm(bool cancel)
        {
            this.DialogResult = cancel ? DialogResult.Cancel : DialogResult.OK;

            this.Hide();
        }

        /// <summary>
        /// Функция вызываемая при нажатии на кнопку "Ок".
        /// </summary>
        /// <param name="sender">Отправитель.</param>
        /// <param name="e">Событие.</param>
        private void ButOk_Click(object sender, EventArgs e)
        {
            this.CloseForm(false);
        }

        /// <summary>
        /// Функция вызываемая при нажатии на кнопку "Отмена".
        /// </summary>
        /// <param name="sender">Отправитель.</param>
        /// <param name="e">Событие.</param>
        private void ButCancel_Click(object sender, EventArgs e)
        {
            this.CloseForm(true);
        }

        #region Элементы

        /// <summary>
        /// Функция запускаемая при выделении элемента в списке.
        /// </summary>
        /// <param name="sender">Отправитель.</param>
        /// <param name="e">Событие.</param>
        private void ListItems_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (-1 == listItems.SelectedIndex)
            {
                return;
            }

            this.UpdateListViewSubItems(false);

            this.Editor.Rows.Clear();
        }

        #endregion

        #region Подэлементы

        /// <summary>
        /// Обновляет данные списка подэлементов.
        /// </summary>
        /// <param name="saveSelect">Сохранять ли выделение элемента.</param>
        private void UpdateListViewSubItems(bool saveSelect = true)
        {
            if (-1 == listItems.SelectedIndex)
            {
                return;
            }

            // Запоминаем индекс выделенного элемента в ListView, так как после обновления выделение сбрасывается.
            int rememberSelSubItemIndex = (listViewSubItems.SelectedIndices.Count > 0) ? listViewSubItems.SelectedIndices[0] : -1;
            
            var selItem = (Item)listItems.SelectedItem;

            listViewSubItems.Items.Clear();

            for (int i = 0; i < selItem.SubItems.Count; ++i)
            {
                listViewSubItems.Items.Add(new ListViewItem(
                    new string[] 
                    {
                        selItem.SubItems[i].Name,
                        selItem.SubItems[i].GetStringValues()
                    }));
            }

            if (saveSelect && rememberSelSubItemIndex > -1 && rememberSelSubItemIndex < listViewSubItems.Items.Count)
            {
                listViewSubItems.SelectedIndices.Add(rememberSelSubItemIndex);
            }
        }

        /// <summary>
        /// Функция запускаемая при изменении выделения в ListViewSubItems.
        /// </summary>
        /// <param name="sender">Отправитель.</param>
        /// <param name="e">Событие.</param>
        private void ListViewSubItems_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.FillEditor();
        }
        
        #endregion

        #region Редактирование

        /// <summary>
        /// Заполняет редактор конфигурации подэлемента.
        /// </summary>
        private void FillEditor()
        {
            if (listItems.SelectedIndex == -1 || listViewSubItems.SelectedItems.Count < 1)
            {
                return;
            }

            var selItem = this.conf.Items[listItems.SelectedIndex];
            var selSubItem = selItem.GetSubItemWithName(listViewSubItems.SelectedItems[0].Text);

            Editor.Rows.Clear();

            foreach (SubItem subItem in selItem.SubItems)
            {
                // Нельзя добавить значение элемента в сам элемент
                if (subItem == selSubItem)
                {
                    continue;
                }

                var added = false; // Произошло ли добавление подэлемента в редактор

                foreach (Pair pair in selSubItem.Values)
                {
                    if (pair.Val == subItem)
                    {
                        Editor.Rows.Add(subItem.Name, pair.Sign == Signal.plus, pair.Sign == Signal.minus);
                        added = true;

                        break;
                    }
                }

                // Если не добавили со значениями, добавляем без значений
                if (!added)
                {
                    Editor.Rows.Add(subItem.Name, false, false);
                } 
            }

            Editor.ClearSelection();
        }

        /// <summary>
        /// Функция запускаемая при изменении значения ячейки редактора конфигурации.
        /// </summary>
        /// <param name="sender">Отправитель.</param>
        /// <param name="e">Событие.</param>
        private void Editor_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            // Если элементов нет, выходим
            if (Editor.Rows.Count < 1)
            {
                return;
            }

            // Нельзя выделить и плюс и минус одновременно
            if (Editor[1, e.RowIndex].Value.ToString() == true.ToString() && Editor[2, e.RowIndex].Value.ToString() == true.ToString())
            {
                switch (e.ColumnIndex)
                {
                    case 1:
                        {
                            Editor[1, e.RowIndex].Value = true;
                            Editor[2, e.RowIndex].Value = false;

                            break;
                        }

                    case 2:
                        {
                            Editor[2, e.RowIndex].Value = true;
                            Editor[1, e.RowIndex].Value = false;

                            break;
                        }
                }
            }

            this.ChangeConfigurationSubItem(e.RowIndex);

            Editor.ClearSelection();
        }

        /// <summary>
        /// Изменяет конфигурацию подэлемента.
        /// </summary>
        /// <param name="rowIndex">Выделенная строка в редакторе конфигурации подэлемента.</param>
        private void ChangeConfigurationSubItem(int rowIndex)
        {
            var selItem = this.conf.Items[listItems.SelectedIndex];
            var selSubItem = selItem.GetSubItemWithName(listViewSubItems.SelectedItems[0].Text);

            // Если нет знака удаляем пару с таким значеним и выходим
            if ((bool)Editor[1, rowIndex].Value == false && (bool)Editor[2, rowIndex].Value == false)
            {
                for (int i = 0; i < selSubItem.Values.Count; ++i)
                {
                    if (selSubItem.Values[i].Val.Name == Editor[0, rowIndex].Value.ToString())
                    {
                        selSubItem.Values.Remove(selSubItem.Values[i]);

                        break;
                    }
                }

                this.UpdateListViewSubItems();

                return;
            }

            // Если знак есть ищем в уже имеющихся парах
            var changed = false; // Произошло изменение

            var sign = (Editor[1, rowIndex].Value.ToString() == true.ToString()) ? Signal.plus : Signal.minus;
            
            for (int i = 0; i < selSubItem.Values.Count; ++i)
            {
                if (selSubItem.Values[i].Val.Name == Editor[0, rowIndex].Value.ToString())
                {
                    selSubItem.Values[i].Sign = sign;
                    changed = true;

                    break;
                }
            }

            // Если в имеющихся элемента не нашлось, добавляем
            if (!changed)
            {
                selSubItem.AddPair(new Pair(selItem.GetSubItemWithName(Editor[0, rowIndex].Value.ToString()), sign));
            }

            this.UpdateListViewSubItems();
        }

        /// <summary>
        /// Функция запускаемая при изменении значения ячейки в связи с ее содержимым.
        /// </summary>
        /// <param name="sender">Отправитель.</param>
        /// <param name="e">Событие.</param>
        private void Editor_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            // Вызываем CellValueChanged
            Editor.CommitEdit(DataGridViewDataErrorContexts.Commit);
        }

        #endregion
    }
}